/*
 * capaDireccionamiento.c
 *
 *  Created on: 19/11/2011
 *      Author: utn_so
 */

#include "capadireccionamiento.h"

extern t_pfs pPfs;

t_sector * pedirSector(int32_t nSector){
	t_sector * sector = (t_sector *)malloc(sizeof(t_sector));
		sector->numeroSector = nSector;

	t_file_block * bloque=fileio_readfileblock("/home/utn_so/Escritorio/fat32.disk4",512,sector->numeroSector);

	char * data= (char *) malloc(512);
	memcpy(data,bloque->data,512);
	sector->data= data;
	fileio_destroyfileblock(bloque);
	return sector;
}

int32_t	pedirSectorArmado(t_sector * sectorArmado){
	t_sector * sectorAux;
	sectorAux = pedirSector(sectorArmado->numeroSector);
	sectorArmado->data   = sectorAux->data;
	free(sectorAux);

	return 1;
}

t_sector * pedirBootSector(void){
	t_sector * bootSector;
	bootSector = pedirSector(0);
	return bootSector;
}

t_cluster * pedirCluster(nCluster){
t_cluster * clusterPedido = armarCluster(nCluster);
pedirClusterArmado(clusterPedido);

return clusterPedido;
}

int32_t pedirClusterArmado(t_cluster * clusterArmado){
	collection_list_iterator(clusterArmado->bloques,pedirBloqueArmado);
	return 1;
}


int32_t pedirBloqueArmado(t_bloque * bloqueAPedir){
	collection_list_iterator(bloqueAPedir->sectores,pedirSectorArmado);
	return 1;


}









t_cluster * armarCluster(int32_t nCluster){
t_cluster * nuevoCluster = (t_cluster *)malloc(sizeof(t_cluster));
nuevoCluster->numeroCluster=nCluster;
nuevoCluster->bloques = armarBloquesDeCluster(nuevoCluster);
return nuevoCluster;
}

t_list * armarBloquesDeCluster(t_cluster * cluster){
int32_t i;
t_list * listaDeBloques = collection_list_create();
t_bloque *bloque;

for(i=0; i< (pPfs.fat32.bootSector.sectorsPerCluster / pPfs.configArch.blockSize);i++){
	bloque = (t_bloque *)malloc(sizeof(t_bloque));
	bloque->nBloque = i;
//bloque->nBloqueT = (cluster->numeroCluster * pPfs.fat32.bootSector.sectorsPerCluster / pPfs.configArch.blockSize / i);
	bloque->nBloqueT = (cluster->numeroCluster * (pPfs.fat32.bootSector.sectorsPerCluster / pPfs.configArch.blockSize) + i);
	bloque->nCluster = cluster->numeroCluster;
	bloque->sectores=armarSectoresDeBloque(bloque);
	collection_list_add(listaDeBloques, bloque);
}
return listaDeBloques;
}

t_list * armarSectoresDeBloque(t_bloque * bloque){
	int32_t i;
	t_list * listaDeSectores = collection_list_create();
	t_sector *sector;

	for(i=0; i<pPfs.configArch.blockSize;i++){
		sector = (t_sector *)malloc(sizeof(t_sector));
//		sector->numeroSector = (bloque->nBloqueT -1) * pPfs.fat32.bootSector.sectorsPerCluster + i;
//		sector->numeroSector = (bloque->nBloqueT ) * (pPfs.fat32.bootSector.sectorsPerCluster / pPfs.configArch.blockSize) +i;
	//	sector->numeroSector = (bloque->nCluster) * (pPfs.fat32.bootSector.sectorsPerCluster) + bloque->nBloque * pPfs.configArch.blockSize +i;
		sector->numeroSector = calcularLogicalSectorNumber(bloque->nCluster)+bloque->nBloque * pPfs.configArch.blockSize+i;
		collection_list_add(listaDeSectores, sector);
	}
	return listaDeSectores;
}

void freeSector(t_sector * sector){
	free(sector->data);
	free(sector);
}
void freeBloque(t_bloque * bloque){
	//collection_list_removeAndDestroy(bloque->sectores,freeSector);
	free(bloque->sectores);
	free(bloque);
}
void freeCluster(t_cluster * cluster){

	//collection_list_removeAndDestroy(cluster->bloques,freeBloque);
	free(cluster->bloques);
	free(cluster);


}

int32_t escribirCluster(t_cluster * clusterAEscribir){
	collection_list_iterator(clusterAEscribir->bloques,escribirBloque);
}

int32_t escribirBloque(t_bloque * bloqueAEscribir){
	collection_list_iterator(bloqueAEscribir->sectores,escribirSector);
}

int32_t escribirSector(t_sector * sectorAEscribir){
	FILE * pFile;
	  pFile = fopen ("/home/utn_so/Escritorio/fat32.disk4","w");
	  if (pFile!=NULL)
	  {
	    fwrite(pPfs.fat32.fat->tabla,1,512,&pFile[(sectorAEscribir->numeroSector-1)*512]);

	    fclose (pFile);
	  }

}

t_list * listaDeClusters(t_list * clustersAPedir){
int i=0;
t_list * listaDeClustersArmados = collection_list_create();

while(i<clustersAPedir->elements_count){
	int32_t * clusterAPedir = collection_list_get(clustersAPedir,i);
	//leerCluster()
	t_cluster * clusterArmado = armarCluster(*clusterAPedir);
	pedirClusterArmado(clusterArmado);
	collection_list_add(listaDeClustersArmados,clusterArmado);
	i++;
}



return listaDeClustersArmados;
}


int32_t cacheActivada(void){
	return pPfs.configArch.tamCache !=0;
}

int32_t bytesPerCluster(void){
	return pPfs.fat32.bootSector.bytesPerSector * pPfs.fat32.bootSector.sectorsPerCluster;
}
//QUEDA
char * leerBloque(int32_t nBloque){
char * dataBloque =(char *) malloc(bytesPerBlock());
int sectorLeido =0;
int offset=0;

for(;sectorLeido < pPfs.configArch.blockSize ;sectorLeido++){
memcpy( dataBloque + offset,leerSector(nBloque * pPfs.configArch.blockSize + sectorLeido),pPfs.fat32.bootSector.bytesPerSector);
offset = offset + pPfs.fat32.bootSector.bytesPerSector;

}
return dataBloque;

}
//QUEDA
char * leerBloqueDisco(int32_t nBloque){
char * dataBloque =(char *) malloc(bytesPerBlock());
int sectorLeido =0;
int offset=0;

for(;sectorLeido < pPfs.configArch.blockSize ;sectorLeido++){
memcpy( dataBloque + offset,leerSector(nBloque * pPfs.configArch.blockSize + sectorLeido),pPfs.fat32.bootSector.bytesPerSector);
offset = offset + pPfs.fat32.bootSector.bytesPerSector;

return dataBloque;
}



}

char* leerSector(int32_t nSector){
	t_sector * sector = (t_sector *)malloc(sizeof(t_sector));
		sector->numeroSector = nSector;

	t_file_block * bloque=fileio_readfileblock("/home/utn_so/Escritorio/fat32.disk4",512,sector->numeroSector);

	char * data= (char *) malloc(512);
	memcpy(data,bloque->data,512);
	fileio_destroyfileblock(bloque);
	return data;
}

int32_t bytesPerBlock(void){
	return (pPfs.fat32.bootSector.bytesPerSector * pPfs.configArch.blockSize);
}



void escribirBloque_v2(uint32_t nBloque, char * dataBloque){
	int32_t sectorEscrito;
	int32_t offset = 0;

	for(sectorEscrito =0 ;sectorEscrito< pPfs.configArch.blockSize ;sectorEscrito++){
	escribirSector_v2(nBloque * pPfs.configArch.blockSize + sectorEscrito,dataBloque+offset);
	offset = offset + pPfs.fat32.bootSector.bytesPerSector;
	}
}

void escribirSector_v2(uint32_t nSector,char *dataSector){
	 FILE * pFile = fopen("/home/utn_so/Escritorio/fat32.disk4", "w");
	 if(pFile == NULL)
	   {
	     printf("Error opening %s for writing. Program terminated.", "/home/utn_so/Escritorio/fat32.disk4");
	     abort();
	   }

	 fwrite(dataSector,pPfs.fat32.bootSector.bytesPerSector,1,pFile);
	//t_file_block * bloque=fileio_("/home/utn_so/Escritorio/fat32.disk4",512,nSector);
}
//queda
t_list * listaBloquesDeCluster(int32_t nCluster){
	uint32_t nBloqueInicial;
	uint pos=0;
	uint32_t * nBloqueAPedir;

	t_list * bloquesApedir = collection_list_create();
	nBloqueInicial = obtenerNumeroDeBloque(nCluster);
	for(pos=0;pos< cantidadDeBloquesPorCluster();pos++){

					nBloqueAPedir = malloc(sizeof(uint32_t));
					*nBloqueAPedir = nBloqueInicial +pos;
					collection_list_add(bloquesApedir,nBloqueAPedir);
				}

	return bloquesApedir;
}

	//agrega pntero a de los bloques
t_list * siElBloqueEstaEnCacheSubir(t_list * bloquesApedir,t_cache_archivo * cachArchivo){
	uint32_t  nBloqueAnalizado,nBloqueAnalizadoEnCache=0;
	uint32_t * nBloqueEnLista;
	t_list * listaAuxiliar= collection_list_create();
	t_cache_bloqueDeArchivo * bloqueCache;
	t_list *bloquesAPedirQueNoEstanEnCache=collection_list_create();
	t_list * listaPosicionesA;
	int32_t encontrado=0;

	//por cada bloque a pedir
	while(nBloqueAnalizado < bloquesApedir->elements_count){
		uint32_t nBloqueAnalizado=0;
		nBloqueEnLista = collection_list_get(bloquesApedir,nBloqueAnalizado);

		//por cada bloque en la cache
		while(nBloqueAnalizadoEnCache < cantidadBloquesPorCache()&& !encontrado){
			bloqueCache = collection_list_get(cachArchivo->listaBloquesCache,nBloqueAnalizado);
			if (bloqueCache->nBloque == nBloqueEnLista){//saco y pongo al principio de la lista
				bloqueCache = collection_list_remove(cachArchivo->listaBloquesCache,nBloqueAnalizado);
				collection_list_add(cachArchivo->listaBloquesCache,bloqueCache);
				encontrado=1;
			}
			nBloqueAnalizadoEnCache++;
		}
		if(!encontrado){
			uint32_t * nbloque = malloc(sizeof(uint32_t));
			*nbloque = bloqueCache->nBloque;
			collection_list_add(bloquesAPedirQueNoEstanEnCache,nbloque);
		}
		encontrado =0;
		nBloqueAnalizadoEnCache = 0;
		nBloqueAnalizado++;
	}

}
//QUEDA
char * leerClusterEnCache(uint32_t nCluster,t_cache_archivo * archivoCache){
	char  * clusterData = malloc (bytesPerCluster());

uint32_t nBloqueAnalizado = obtenerNumeroDeBloque(nCluster);
uint32_t nCantidadBloquesAnalizados=0;
int32_t offset=0;

while(nCantidadBloquesAnalizados < cantidadDeBloquesPorCluster()){
	t_cache_archivo * bloqueCache;
	bloqueCache = leerBloqueCache(nBloqueAnalizado,archivoCache);
	memcpy(&clusterData[offset],bloqueCache,bytesPerBlock());
	offset = offset + bytesPerBlock();
	}
//me pa que hay que liberar el bloque ahora;



return clusterData;
}



//QUEDA
char* leerCluster(uint32_t nCluster,t_cache_archivo * cacheArchivo) {
	char* cluster, *bloque;
	cluster = (char *) malloc(bytesPerCluster());
	t_list *bloquesQueNoEstanEnCache;
	int32_t nBloqueInicial, nBloqueAnalizado, j;

	if(strcmp(cacheArchivo->pathYNombreDelArhivo,'/')){
		if(cacheActivada()){
			t_list * bloquesApedir;
		bloquesApedir = listaBloquesDeCluster(nCluster);
			//hago lista de bloques a fijarme
		bloquesQueNoEstanEnCache = siElBloqueEstaEnCacheSubir(bloquesApedir,cacheArchivo);
		uint32_t* bloqueALeerDeDisco;
		uint32_t nBloqueALeerDeDisco = 0;

		while(nBloqueALeerDeDisco < bloquesQueNoEstanEnCache->elements_count){
			bloqueALeerDeDisco = collection_list_get(bloquesQueNoEstanEnCache,nBloqueALeerDeDisco);
			t_cache_bloqueDeArchivo * nuevoBloque;

			//TODO:....mmm....nose si funca, hay que revisar devuelta toda la estructura

			nuevoBloque->data=leerBloqueDisco(*bloqueALeerDeDisco);
			nuevoBloque->nBloque;
			nuevoBloque->modificado=0;
			escribirBloqueCache(cacheArchivo,nuevoBloque);
		}
		char * clusterData = leerClusterEnCache(nCluster,cacheArchivo);

}
	}
	//SI LA CACHE NO ESTA ACTIVADA LEO DE DISCO EL CLUSER....MAS FACIL NO?
	uint32_t cantidadLeida = 0;

	for (nBloqueAnalizado = nBloqueInicial; nBloqueAnalizado < nBloqueInicial + cantidadDeBloquesPorCluster(); nBloqueAnalizado++) {
		bloque = leerBloque(nBloqueAnalizado);
		memcpy(cluster + cantidadLeida, bloque, 1024);
		cantidadLeida = cantidadLeida + bytesPerBlock();
		free(bloque);
	}
	return cluster;
}
//queda
int32_t estaBloqueEnCache(int32_t nBloque, t_cache_archivo * cacheArchivo){
	int32_t encontrado=0;
	uint32_t nBLoqueAnalizado =0;
	t_bloque * bloque;
	while(!encontrado && nBLoqueAnalizado < cantidadBloquesPorCache()){
		bloque = collection_list_get(cacheArchivo->listaBloquesCache,nBLoqueAnalizado);
		if(bloque->nBloque == nBloque){
			encontrado = 1;
		}

	}
	return encontrado;

}

void escribirClusterV2(char * clusterData,uint32_t nCluster){
	uint32_t nBloqueActual = 	obtenerNumeroDeBloque(nCluster);
	uint32_t offset =0;
	for(;nBloqueActual < cantidadDeBloquesPorCluster();nBloqueActual++){
		escribirBloque_v2(nBloqueActual,clusterData+offset);
		offset = offset + bytesPerBlock();
	}

}
